package org.bhf.service;

/**
 * <p>Implementations are responsible for routing an incoming request to a (serviceGroup,service,method,[id]) tuple based on
 * route specifications, or patterns. For a s
 */
public interface URLRouter
{
    /**
     * Attempt to find a route for the given URI. When more than one route matches the URI, the first is chosen.
     *
     * @param extenalURI The incoming URI to be matched.
     * @param hostName If the project is not specified in the URI, it will be taken as the subdomain. <code>hostName</code>
     *  must be a name, not an IP address.
     * @return The route, or <code>null</code> if no route exists.
     */
    RouteMatch  route( String extenalURI, String hostName );

    /**
     * Return the most specific route to this Service or Component method. If more than one route can be used to
     * each this method, the most specific will be chosen. Route A is more specific than route B, if A has more
     * of the route 'hardwired' with key/value pairs than route B. If routes are equalls specific, the first is chosen.
     *
     * @param service Service class, Msut not be <code>null</code>
     * @param method Simple method name. Must not be <code>null</code>
     * @param projectName If <code>null</code> the ID of the <code>ContextProject</code> will be used.
     * @return The URI that can be used to reach this Service or Component method.
     * @throws RuntimeException If no route can be found.
     */
    String      reverseRoute( Class<?> service, String method, String projectName );

    /**
     * Return the most specific route to this Component method. If more than one route can be used to
     * each this method, the most specific will be chosen. Route A is more specific than route B, if A has more
     * of the route 'hardwired' with key/value pairs than route B. If routes are equalls specific, the first is chosen.
     *
     * @param component Component object, Msut not be <code>null</code>
     * @param method Simple method name. Must not be <code>null</code>
     * @param projectName If <code>null</code> the ID of the <code>ContextProject</code> will be used.
     * @return The URI that can be used to reach this Component method.
     * @throws RuntimeException If no route can be found.
     */
    String      reverseRoute( Component component, String method, String projectName );

    /**
     * Return the most specific route to this Component method. If more than one route can be used to
     * each this method, the most specific will be chosen. Route A is more specific than route B, if A has more
     * of the route 'hardwired' with key/value pairs than route B. If routes are equalls specific, the first is chosen.
     *
     * @param service Component object, Must not be <code>null</code>
     * @param method Simple method name. Must not be <code>null</code>
     * @param projectName If <code>null</code> the ID of the <code>ContextProject</code> will be used.
     * @param id Unique id if this Service is a Component, or -1 if this Service is simply a Service.
     * @return The URI that can be used to reach this Component method.
     * @throws RuntimeException If no route can be found.
     */
    String      reverseRoute( Class<?> service, String method, String projectName, long id );

    /**
     * Result of a route match. All properties, except possible the Component id, will be appropriately
     * populated.
     */
    public final static class RouteMatch
    {
        String  serviceGroup;
        String  service;
        String  method;
        String  project;
        String  context;
        boolean antiRoute;
        long    id;

        public RouteMatch( final String serviceGroup, final String service, final String method, final long id, final String context, final String project )
        {
            this.serviceGroup   = serviceGroup;
            this.service        = service;
            this.method         = method;
            this.project        = project;
            this.context        = context;
            this.id             = id;
        }

        /**
         * <code>true</code> if this is an anti-route and should be passed on by the controller.
         * @return <code>true</code> if this is an anti-route and should be passed on by the controller.
         */
        public boolean  isAntiRoute()
        {
            return antiRoute;
        }

        public void     setAntiRoute( final boolean antiRoute )
        {
            this.antiRoute = antiRoute;
        }

        /**
         * Return the part of the servlet context that matches the route.
         * @return The route context.
         */
        public String   getContext()
        {
            return context;
        }

        public void     setContext( final String context )
        {
            this.context = context;
        }

        /**
         * The name of the Module to be invoked. Matches the name in the <code>ModuleInfo</code> annotation.
         * @return The name of the Module to be invoked.
         */
        public String   getServiceGroup()
        {
            return serviceGroup;
        }

        public void     setServiceGroup( final String serviceGroup )
        {
            this.serviceGroup = serviceGroup;
        }

        /**
         * The name of the Service/Component to be invoked. Matches the <code>ServiceName</code> annotation.
         * @return The name of the Service/Component to be invoked.
         */
        public String   getService()
        {
            return service;
        }

        public void     setService( final String service )
        {
            this.service = service;
        }

        /**
         * return the project name associated with this request.
         * @return The project name associated with this request.
         */
        public String   getProject()
        {
            return project;
        }

        public void     setProject( final String project )
        {
            this.project = project;
        }

        /**
         * The method (action) to be invoked.
         * @return the method (action) to be invoked.
         */
        public String   getMethod()
        {
            return method;
        }

        public void     setMethod( final String method )
        {
            this.method = method;
        }

        /**
         * May be -1 if this route match does not have an id, indicting that the target is to be invoked as a service rather
         * than a component.
         * @return The component id. May be -1.
         */
        public long     getId()
        {
            return id;
        }

        public void     setId( final long id )
        {
            this.id = id;
        }
    }
}
